package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.Scanner;

public class Server implements ServerActions {

	/** ServerSocket! Important for a Server! :) */
	private static ServerSocket _ss;
	/** This is a Thread that sends a message to all the Clients	 */
	private static OutPutThread _output;
	/** This is used for synchronization purposes. Could also use the keyword
	 * synchronized if you want to do more research on it in the API
	 */
	private static boolean _blockRequest;
	
	/** Similar to above	 */
	private static boolean _blockRun;
	/** Message to send to each client */
	private static String _outMessage;
	
	/** List of Clients */
	private static LinkedList<ServerThread> _threads;
	
	/**A reference to the portNumber */
	private static int portNo = 4242;
	
	/** Adds a Client to the List of Clients
	 * @param st Thread that has the Clients Socket Info
	 */
	public static void addThread(ServerThread st){
		//This ensures that only one person can access the List at the same time. Else
		//it can destroy data. Look up the synchronized keyword to have this done for you
		while(_blockRequest)
			try {
				//Have this Thread Sleep for a 1000 milliseconds so it doesnt eat up CPU
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				//Ignor this exception... might want to put something here if the server is
				//crashing though.
			}
			//Lock the key
		_blockRun = true;
		//Add the Client
		_threads.add(st);
		//Send Connected message
		st._out.println("Connected!");
		//Release Lock
		_blockRun = false;		
		
	}
	
	/**
	 * Send a Message to every Client connected
	 * @param s String to Send
	 */
	public static void sendMessage(String s){
		//Wait for Out Message to be cleared
		while(_outMessage != null)
			try {
				//Don't eat up CPU while I wait
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				//Ignore Error go back in loop
			}
		while(_blockRequest)
			try {
				//See Above
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				//See Above
			}
			//Lock
		_blockRun = true;
		//Set Message
		_outMessage = s;
		//UnLock
		_blockRun = false;	
	}
		
	/**
	 * The OutPutThread that sends messages to the clients
	 * @author jcbbjjttt
	 *
	 */
	static class OutPutThread implements Runnable{
		
		/** Initialize everything
		 * 
		 */
		public OutPutThread(){
			_blockRequest = false;
			_blockRun = false;
			_threads = new LinkedList<ServerThread>();
		}
		//Runs the thread
		public void run() {
			//Should probably not do a while true here. Need some way to kill the thread without
			//a force quit. You figure it out :-P
			while(true){
				//Wait for Locks and a Message
				if(!_blockRun && _outMessage != null){
					//Lock
					_blockRequest = true;
					//Send Message to Each Thread
					for(ServerThread st: _threads){
						st._out.println(_outMessage);
					}
					//Clear out the Message
					_outMessage = null;
					//UnLock
					_blockRequest = false;
				}
				try {
					//Don't kill CPU in Loop
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					//Come Up with something to put here
				}
			}
			
		}
		
		
	}
	
	/**
	 * A Thread that listens for clients and connects them to the Server
	 * @author jcbbjjttt
	 *
	 */
	static class ListeningThread implements Runnable{

		public void run() {
			try {
				//Set ServerSocket to portNo some computers might not like this. Most will
				_ss = new ServerSocket(portNo);
				while(true){
					//When Someone Connects Create a New ServerThread to Listen to them
					new Thread(new ServerThread(_ss.accept())).start();
				}		
			} catch (IOException e) {
				//This will happen if the socket is not open
			}
				
		}
		
	}
	
	/**
	 * Thread that Listens to a Client for a message
	 * @author jcbbjjttt
	 *
	 */
	static class ServerThread implements Runnable{
		/** It gets its own Socket!	 */
		private Socket _socket;
		/** A Writer to send data to the Client */
		private PrintWriter _out;
		/** A Reader to get data from the Client */
		private BufferedReader in;
		
		
		/**
		 * Initialize thread on a Socket
		 * @param s Socket to Connect to
		 */
		public ServerThread(Socket s){
			//Set Socket
			_socket = s;
			try {
				//Try to connect get output and input
				_out = new PrintWriter(_socket.getOutputStream(), true);				
				in = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
				
			} catch (IOException e) {
				//If you cannot connect set to null
				_out = null;
				in = null;
			}
			
		}

		//RUN THREAD! WOOOO!
		public void run() {
			
			try {
				//Add this Thread
				addThread(this);
				//Set a String to get messages
				String message;
				while(true){
					//When I get a message
					if((message = in.readLine()) != null){
						//Send it!
						sendMessage(message);
					}
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
					}
				}
			} catch (IOException e) {
				System.err.println("Thread Died...");
			}
			System.err.println("Thread Finished...");
		}
		
	}
	public static void printIP() throws UnknownHostException{
		String s = InetAddress.getLocalHost().toString();
		String ip = s.substring(s.indexOf("/") +1, s.length());
		System.out.println("Running on IP: " + ip);
	}
	
	public static void promptPort(){
		Scanner keyboard = new Scanner(System.in);
		System.out.println("Please insert port number or press 'n' to use default:" + portNo);
		String port = keyboard.next();
		if(!port.equals("n")){
		int pN = Integer.parseInt(port);
		portNo = pN;
		}
	}
	
	public static void printPort(){
		System.out.println("Runing on Port: " + portNo);
	}
	/** Launch the Server! Simple Main... most programs should only have a few lines in main :-P
	 * @param args
	 * @throws UnknownHostException 
	 * prints IP and portNumber in case portForwarding is set to that IP and PortNo
	 */
	public static void main(String[] args) throws UnknownHostException{
		System.out.println("Starting Server....");
		new Thread(new ListeningThread()).start();
		_output = new OutPutThread();
		new Thread(_output).start();
		System.out.println("Server Running....");
		printIP();
		promptPort();
		printPort();
	}
	@Override
	public void addUser(String user) {
		// TODO Auto-generated method stub

	}

	@Override
	public void removeUser(String user) {
		// TODO Auto-generated method stub

	}

	@Override
	public void validUser(String user) {
		// TODO Auto-generated method stub

	}

	@Override
	public void privateMessage(String message) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void sendMoney(int money, String sender, String receiver) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void sendTower(Object tower, String sender, String receiver) {
		// TODO Auto-generated method stub
		
	}

}
